﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ADDDF.Infrastructure.MemoryBound;
using ADDDF.Infrastructure;

namespace ADDDF.Infrastructure.Test.MemoryBoundRepositoryFramework
{
    [TestClass]
    public class MemoryBoundRepositoryBaseUnitTest
    {
        [TestMethod]
        public void MemoryBoundRepositoryBase_EntityWithIntegerKey()
        {
            MBDataStore.Reset();

            Movie m1 = new Movie(EntityHelper.NewKey(), "Gone with the wind");

            IMovieRepository repo = RepositoryFactory.Create<IMovieRepository, Movie>();

            repo.Add(m1);

            Assert.IsNotNull(m1.Key);

            Movie m2 = repo.FindByKey(m1.Key);

            Assert.IsNotNull(m2);
            Assert.AreEqual(m1.Key, m2.Key);
            Assert.AreEqual(m1.Name, m2.Name);

            m2.Name = "Star Wars";

            Assert.AreNotEqual(m1.Name, m2.Name);

            repo.Change(m2);

            Movie m3 = repo.FindByKey(m1.Key);

            Assert.AreEqual(m3.Name, m2.Name);
            Assert.AreNotEqual(m3.Name, m1.Name);

            repo.Remove(m3);

            Assert.IsNull(repo.FindByKey(m3.Key));
        }

        [TestMethod]
        public void MemoryBoundRepositoryBase_EntityWithCompositeKey()
        {
            DateTime date1 = new DateTime(2010, 10, 30);
            int accKey1 = 10;
            decimal amount1 = 230;

            DateTime date2 = new DateTime(2010, 11, 30);
            decimal amount2 = 630;

            MBDataStore.Reset();

            IAccountEntryRepository repo = RepositoryFactory.Create<IAccountEntryRepository, AccountEntry>();

            AccountEntry m6 = new AccountEntry(accKey1, date2, amount2);

            repo.Add(m6);

            AccountEntry m1 = new AccountEntry(accKey1, date1, amount1);

            repo.Add(m1);

            Assert.IsInstanceOfType(m1.Key, typeof(AccountEntryKey));

            AccountEntryKey key1 = new AccountEntryKey(m1.AccountKey, m1.EntryNo);

            Assert.AreEqual(key1.AccountKey, accKey1);
            Assert.AreEqual(key1, m1.Key);

            AccountEntry m2 = repo.FindByKey(key1);

            Assert.IsNotNull(m2);
            Assert.AreEqual(m1.Key, m2.Key);
            Assert.AreEqual(m1.Date, m2.Date);
            Assert.AreEqual(m1.Amount, m2.Amount);

            m2.Amount = 300;

            Assert.AreNotEqual(m1.Amount, m2.Amount);

            repo.Change(m2);

            AccountEntry m3 = repo.FindByKey(m1.Key);

            Assert.AreEqual(m3.Amount, m2.Amount);
            Assert.AreNotEqual(m3.Amount, m1.Amount);

            repo.Remove(m3);

            Assert.IsNull(repo.FindByKey(m3.Key));
        }
    }

    #region "An Entity with Integer key and its Repositories"

    public class Movie : KeyAssignableEntityBase, IAggregateRoot, ICloneable
    {
        private string _name;

        public Movie(object key, string name)
            : base(key)
        {
            _name = name;
        }

        public Movie(int id, string name)
            : base(id)
        {
            _name = name;
        }

        public int MovieID
        {
            get { return (int)this.Key; }
        }

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public object Clone()
        {
            return new Movie(this.Key, _name);
        }
    }

    public interface IMovieRepository : IRepository<Movie>
    {
    }

    public class MemoryBoundMovieRepository : MBRepositoryBase<Movie>, IMovieRepository
    {
    }

    #endregion

    #region "An Entity with composite key and its Repositories"

    public class AccountEntryKey : IValueObject
    {
        private int _accKey;
        private int _entryNo;

        public AccountEntryKey(int accountKey, int entryNo)
        {
            _accKey = accountKey;
            _entryNo = entryNo;
        }

        public int AccountKey { get { return _accKey; } }

        public int EntryNo { get { return _entryNo; } }

        public override bool Equals(object another)
        {
            AccountEntryKey p = another as AccountEntryKey;
            if ((object)p == null)
            {
                return false;
            }

            return _accKey == p._accKey && _entryNo == p._entryNo;
        }

        public override int GetHashCode()
        {
            return _accKey.GetHashCode() ^ _entryNo.GetHashCode();
        }
    }

    public class AccountEntry : KeyAssignableEntityBase, IAggregateRoot
    {
        private DateTime _date;
        private decimal _amount;

        public AccountEntry(int accountKey, DateTime date, decimal amount)
            : base(new AccountEntryKey(accountKey, 0))
        {
            _date = date;
            _amount = amount;
        }

        public AccountEntry(int accountKey, int entryNo, DateTime date, decimal amount)
            : base(new AccountEntryKey(accountKey, entryNo))
        {
            _date = date;
            _amount = amount;
        }

        public DateTime Date { get { return _date; } }

        public decimal Amount
        {
            get { return _amount; }
            set { _amount = value; }
        }

        public int AccountKey { get { return GetKeyAsAccountEntryKey().AccountKey; } }

        public int EntryNo { get { return GetKeyAsAccountEntryKey().EntryNo; } }

        public AccountEntryKey GetKeyAsAccountEntryKey()
        {
            return (AccountEntryKey)this.Key;
        }
    }

    public interface IAccountEntryRepository : IRepository<AccountEntry>
    {
    }

    public class MemoryBoundAccountEntryRepository : MBRepositoryBase<AccountEntry>, IAccountEntryRepository
    {
        protected override object GetNewKey(AccountEntry entity)
        {
            return new AccountEntryKey(entity.AccountKey, this.GetNextIntegerKey());
        }

        protected override AccountEntry CloneAggregateRoot(AccountEntry item)
        {
            AccountEntry cloned;

            if (item.Key == null)
            {
                cloned = new AccountEntry(item.AccountKey, item.Date, item.Amount);
            }
            else
            {
                cloned = new AccountEntry(item.AccountKey, item.EntryNo, item.Date, item.Amount);
            }

            return cloned;
        }
    }

    #endregion
}
